home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / CBlibrary / c / Loader < prev    next >
Encoding:
Text File  |  2003-10-16  |  34.8 KB  |  968 lines

  1. /*
  2.  * CBLibrary - Loader
  3.  * Copyright (C) 2003  Chris Bazley
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2.1 of the License, or (at your option) any later version.
  9.  *
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  */
  19.  
  20. /* Multi-dialogue automatic data transfer receiver */
  21.  
  22. /* 11.09.03 CJB Changed _ldr_check_dropzone() so that NULL_ObjectId rather 
  23.                 than -1 is the marker of a listener that doesn't care which 
  24.                 object the file was dragged to. This changes the functional 
  25.                 definition of loader_register_listener() - sorry.
  26.                 loader_buffer_file() now accepts const character strings as 
  27.                 file_path.
  28. */
  29.  
  30. /* ANSI library files */
  31. #include <stdlib.h>
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <stdbool.h>
  35.  
  36. /* RISC OS library files */
  37. #include "kernel.h"
  38. #include "wimp.h"
  39. #include "event.h"
  40. #include "wimplib.h"
  41. #include "flex.h"
  42.  
  43. /* Toolbox (for dropzones) */
  44. #include "toolbox.h"
  45. #include "iconbar.h"
  46. #include "gadgets.h"
  47. #include "window.h"
  48.  
  49. /* Other headers */
  50. #include "err.h"
  51. #include "msgtrans.h"
  52. #include "hourglass.h"
  53. #include "Macros.h"
  54. #include "Loader.h"
  55. #include "NoBudge.h"
  56.  
  57. #define RAM_BUFFER_SIZE 256
  58. #define RAM_BUFFER_EXTEND 256
  59.  
  60. #define ERR_BAD_OBJECT_ID    0x1b80cb02
  61. #define ERR_BAD_COMPONENT_ID 0x1b80a914
  62.  
  63. extern _kernel_oserror shared_err_block;
  64.  
  65. /* -----------------------------------------------------------------------
  66.                           Internal data structures
  67. */
  68.  
  69. typedef struct _LoaderListenerBlk
  70. {
  71.   int flags;
  72.   /* Filetype/destination to listen for */
  73.   int          file_type;
  74.   ObjectId     object;
  75.   ComponentId *gadgets; /* malloc block, if NO_STATIC_GADGETS_LIST defined */
  76.   
  77.   /* Client participation */
  78.   LoaderFileHandler     *loader_method;   /* optional */
  79.   LoaderFinishedHandler *finished_method; /* compulsary */
  80.   void                  *client_handle; /* passed to finished_method */
  81.   
  82.   /* Double-linked list */
  83.   struct _LoaderListenerBlk *next_listener;
  84.   struct _LoaderListenerBlk *prev_listener;
  85. } LoaderListenerBlk;
  86.  
  87. /* When listener spawns new dialogue thread: */
  88. typedef struct _LoaderDialogueBlk
  89. {
  90.   LoaderListenerBlk         *parent_listener;
  91.   int                        last_message_ref;
  92.   int                        last_chunk_len;
  93.   WimpMessage               *orig_datasave; /* points to malloc block */
  94.   bool                       RAM_capable;
  95.   int                       *RAM_buffer; /* flex anchor */
  96.   /* Double-linked list */
  97.   struct _LoaderDialogueBlk *prev_dialogue;
  98.   struct _LoaderDialogueBlk *next_dialogue;
  99. } LoaderDialogueBlk;
  100.  
  101. /* -----------------------------------------------------------------------
  102.                         Internal function prototypes
  103. */
  104.  
  105. static void _ldr_replyto_datasave(WimpMessage *reply_to, LoaderListenerBlk *record_listener);
  106. static int _ldr_datasave_listener(WimpMessage *message, void *handle);
  107. static int _ldr_dataloadopen_listener(WimpMessage *message,void *handle);
  108. static int _ldr_ramfetch_bounce_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle);
  109. static int _ldr_ramtransmit_handler(WimpMessage *message,void *handle);
  110. static _kernel_oserror *_ldr_kill_listener(LoaderListenerBlk *kill_listener);
  111. static LoaderListenerBlk *_ldr_find_broadcast_listener(int file_type);
  112. static LoaderListenerBlk *_ldr_find_listener(int file_type, ObjectId window, ComponentId *gadgets);
  113. static LoaderListenerBlk *_ldr_find_suitable_listener(int window_handle, int icon_number, int file_type, int tempfile);
  114. static bool _ldr_check_dropzone(ObjectId object, ComponentId *gadgets,int window_handle, int icon_number);
  115. static LoaderDialogueBlk *_ldr_new_thread(void);
  116. static _kernel_oserror *_ldr_kill_thread(LoaderDialogueBlk *kill_me);
  117.  
  118. /* -----------------------------------------------------------------------
  119.                           Internal library data
  120. */
  121.  
  122. static LoaderListenerBlk *listener_list;
  123. static LoaderDialogueBlk *dialogue_list;
  124. static int _ldr_flags;
  125. /* 
  126.   From: A.Hodgkinson (andrew.hodgkinson@pace.co.uk)
  127.   Subject: Re: Trouble talking to SaveAs object 
  128.   Newsgroups: comp.sys.acorn.programmer
  129.   Date: 2001-06-14 07:50:31 PST 
  130.   
  131.   About the best you can do is associate the suggested leafname with
  132.   the relevant document window you're loading into (if that applies)
  133.   and keep a static copy of said leafname. Whenever a new DataSave
  134.   comes along, if you've not already thrown away the old leafname you
  135.   throw it away now. This does create a potential race condition. For
  136.   example, download two files independently via. direct app-to-app save
  137.   and you'd end up with the leafname of the second always being used,
  138.   even if the second download completed first, loading into the
  139.   document window, followed by the first download loading; that is,
  140.   though, rare and not a critical fault. Or, alternatively, you keep
  141.   a record of my_ref too, and in that case at least *one* of the
  142.   simultaneous data transfers will get a decent title from that leaf,
  143.   even if the rest say '<Wimp$Scrap>'.
  144.   
  145.   At worst you will have transient memory claimed for the leaf name
  146.   that will be discarded at the time you send your next DataSave.
  147.   
  148.   Either way, your DataLoad code shouldn't care about recognising
  149.   the my_ref per se - it should load anyway - even though it might
  150.   check if this is non-zero to alter behaviour for app-to-app
  151.   transfers versus original 'from Filer' loads (where my_ref = 0).
  152.   It might use it to try and ensure the right leafname is picked up
  153.   too, but that's not essential.
  154. */
  155. static char *datasave_leafname; /* should be initialised to NULL */
  156. static int datasaveack_myref;
  157. static LoaderListenerBlk *datasave_listener;
  158.  
  159. /* -----------------------------------------------------------------------
  160.                          Public library functions
  161. */
  162.  
  163. _kernel_oserror *loader_initialise(unsigned int flags)
  164. {
  165.   /* Initialise linked lists */
  166.   listener_list = NULL;
  167.   dialogue_list = NULL;
  168.   _ldr_flags = flags;
  169.  
  170.   datasaveack_myref = -1; /* We don't want our DataLoad handler matching 0 */
  171.   
  172.   /* Set up permanent message handlers for DataSave DataOpen and DataLoad */
  173.   THROW(event_register_message_handler(Wimp_MDataLoad, _ldr_dataloadopen_listener, 0))
  174.   THROW(event_register_message_handler(Wimp_MDataSave, _ldr_datasave_listener, 0))
  175.   if(!FLAG_SET(_ldr_flags, LOADER_IGNOREBCASTS))
  176.     THROW(event_register_message_handler(Wimp_MDataOpen, _ldr_dataloadopen_listener, 0))
  177.  
  178.   /* Ensure that messages are not masked */
  179.   {
  180.     unsigned int mask;
  181.     event_get_mask(&mask);
  182.     CLEAR_FLAG(mask, Wimp_Poll_UserMessageMask);
  183.     CLEAR_FLAG(mask, Wimp_Poll_UserMessageRecordedMask);
  184.     CLEAR_FLAG(mask, Wimp_Poll_UserMessageAcknowledgeMask);
  185.     event_set_mask(mask);
  186.   }
  187.   return NULL; /* success */
  188. }
  189.  
  190. /* ----------------------------------------------------------------------- */
  191. #ifdef INCLUDE_FINALISATION_CODE
  192. _kernel_oserror *loader_finalise(void)
  193. {
  194.   /* Kill all listeners  */
  195.   {
  196.     LoaderListenerBlk *scan_list = listener_list;
  197.     while(scan_list != NULL) {
  198.       THROW(_ldr_kill_listener(scan_list))
  199.       scan_list = scan_list->next_listener;
  200.     }
  201.   }
  202.  
  203.   /* Remove permanent message handlers  */
  204.   THROW(event_deregister_message_handler(Wimp_MDataLoad, _ldr_dataloadopen_listener, 0))
  205.   THROW(event_deregister_message_handler(Wimp_MDataSave, _ldr_datasave_listener, 0))
  206.   if(!FLAG_SET(_ldr_flags, LOADER_IGNOREBCASTS))
  207.     THROW(event_deregister_message_handler(Wimp_MDataOpen, _ldr_dataloadopen_listener, 0))
  208.   return NULL; /* success */
  209. }
  210. #endif
  211. /* ----------------------------------------------------------------------- */
  212.  
  213. _kernel_oserror *loader_register_listener(unsigned int flags, int file_type, ObjectId drop_object, ComponentId *drop_gadgets, LoaderFileHandler *loader_method, LoaderFinishedHandler *finished_method, void *client_handle)
  214. {
  215.   LoaderListenerBlk *newlistener;
  216.   /* Check that proposed key is unique */
  217.   if(_ldr_find_listener(file_type, drop_object, drop_gadgets) != NULL) {
  218.     WRITE_ERR(shared_err_block, "BadListReg");
  219.     return &shared_err_block; /* failure */
  220.   }
  221.   /* Initialise new listener */
  222.   newlistener = malloc(sizeof(LoaderListenerBlk));
  223.   if(newlistener == NULL) {
  224.     WRITE_GERR(shared_err_block, "NoMem");
  225.     return &shared_err_block; /* failure */
  226.   }
  227.   /* Set flags */
  228.   newlistener->flags = flags;
  229.   
  230.   /* Set dropzone */
  231.   newlistener->object = drop_object;
  232.  
  233. #ifdef NO_STATIC_GADGETS_LIST
  234.   /*
  235.      Previously drop_gadgets had to be a static array
  236.      Nowadays we make our own copy just to be on the safe side
  237.   */
  238.   if(drop_gadgets == NULL)
  239.     newlistener->gadgets = NULL;
  240.   else {
  241.     /* count number of gadgets in array */
  242.     int array_len = 0;
  243.     do {
  244.       array_len++;
  245.     } while(drop_gadgets[array_len] != NULL_ComponentId);
  246.  
  247.     /* duplicate the array of gadgets */
  248.     newlistener->gadgets = (ComponentId *)calloc(array_len, sizeof(ComponentId));
  249.     if(newlistener->gadgets == NULL) {
  250.       free(newlistener);
  251.       WRITE_GERR(shared_err_block, "NoMem");
  252.       return &shared_err_block; /* failure */
  253.     }
  254.     memcpy(newlistener->gadgets, drop_gadgets, array_len * sizeof(ComponentId));
  255.   }
  256. #else
  257.   newlistener->gadgets = drop_gadgets;
  258. #endif
  259.   newlistener->file_type = file_type;
  260.   
  261.   /* Set client handlers */
  262.   newlistener->loader_method = loader_method;
  263.   newlistener->finished_method = finished_method;
  264.   newlistener->client_handle = client_handle;
  265.  
  266.   /* Link this Listener onto front of list */
  267.   newlistener->prev_listener = NULL;
  268.   newlistener->next_listener = listener_list;
  269.   if(listener_list != NULL)
  270.     listener_list->prev_listener = newlistener;
  271.   listener_list =  newlistener;
  272.  
  273.   return NULL; /* success */
  274. }
  275.  
  276. /* ----------------------------------------------------------------------- */
  277.  
  278. _kernel_oserror *loader_deregister_listener(int file_type, ObjectId drop_object, ComponentId *drop_gadgets)
  279. {
  280.   LoaderListenerBlk *find_it;
  281.   find_it=_ldr_find_listener(file_type, drop_object, drop_gadgets);
  282.   if(find_it == NULL) {
  283.     WRITE_ERR(shared_err_block, "BadListReg");
  284.     return &shared_err_block; /* failure */
  285.   }
  286.   /* remove the stinking thing */
  287.   if(find_it == datasave_listener) /* CAREFUL!!! */
  288.     datasaveack_myref = -1; /* force re-evaluation of listener */
  289.   return _ldr_kill_listener(find_it);
  290. }
  291.  
  292. /* ----------------------------------------------------------------------- */
  293.  
  294. _kernel_oserror *loader_canonicalise(char **buffer, const char *path_var, const char *path_string, const char *file_path)
  295. {
  296.   /* First pass - determine buffer size needed */
  297.   int spare;
  298.   THROW(_swix(OS_FSControl, _INR(0,5)|_OUT(5), 37, file_path, 0, path_var, path_string, 0, &spare))
  299.  
  300.   char *string = malloc(1 - spare);
  301.   if(string == NULL) {
  302.     WRITE_GERR(shared_err_block, "NoMem");
  303.     return &shared_err_block;
  304.   }
  305.  
  306.   /* Second pass - write canonicalised path */
  307.   _kernel_oserror *e = _swix(OS_FSControl, _INR(0,5), 37, file_path, string, path_var, path_string, 1 - spare);
  308.   if(e != NULL)
  309.     free(string);
  310.   else
  311.     *buffer = string;
  312.   return e;
  313. }
  314.  
  315. /* ----------------------------------------------------------------------- */
  316.  
  317. /* We aren't selfish, so we allow access to our generic file loader
  318. (not quite a LoaderFileHandler because of special provision for sprite files) */
  319.  
  320. _kernel_oserror *loader_buffer_file(const char *file_path, flex_ptr buffer, bool sprite_file)
  321. {
  322.   /* Allocate buffer and load raw data into it... */
  323.   _kernel_osfile_block inout;
  324.   int buffer_size, err;
  325.  
  326.   /* Get size of file */
  327.   if(_kernel_osfile(17, file_path, &inout) == _kernel_ERROR)
  328.     return _kernel_last_oserror(); /* failure */
  329.  
  330.   buffer_size = inout.start;
  331.   if(sprite_file)
  332.     buffer_size += sizeof(int);
  333.  
  334.   /* Allocate buffer for data */
  335.   if(!flex_alloc(buffer, buffer_size)) {
  336.     WRITE_GERR(shared_err_block, "NoMem");
  337.     return &shared_err_block; /* failure */
  338.   }
  339.  
  340.   /* Load file */
  341.   hourglass_on();
  342.   inout.load = (int)*buffer; /* load address */
  343.   if(sprite_file)
  344.     inout.load += sizeof(int);
  345.   inout.exec = 0; /* use specified load address */
  346.   err = _kernel_osfile(16, file_path, &inout);
  347.   hourglass_off();
  348.   
  349.   if(err == _kernel_ERROR) {
  350.     flex_free(buffer);
  351.     return _kernel_last_oserror(); /* failure */
  352.   }
  353.  
  354.   if(sprite_file)
  355.     *(int *)*buffer = buffer_size;
  356.  
  357.   return NULL; /* success */
  358. }
  359.  
  360. /* -----------------------------------------------------------------------
  361.                         Permanent Wimp message listeners
  362. */
  363.  
  364. static int _ldr_datasave_listener(WimpMessage *message, void *handle)
  365. {
  366.   /* We are a permanent DataSave listener (deals with applications) */
  367.   LoaderListenerBlk *listener;
  368.   NOT_USED(handle)
  369.  
  370.   /* This message should come out of the blue */
  371.   if(message->hdr.your_ref != 0) {
  372.     M_RETV("TransUXreq", 0);
  373.   }
  374.  
  375.   /* Are any listeners interested? */
  376.   listener = _ldr_find_suitable_listener(message->data.data_save.destination_window, message->data.data_save.destination_icon, message->data.data_save.file_type, 1);
  377.   if(listener == NULL)
  378.     return 0; /* event not handled */
  379.  
  380.   /* How shall we reply (is RAM transfer allowed?) */
  381.   if(listener->loader_method == NULL) {
  382.     /* Can try RAM transfer (see if they support it) */
  383.   
  384.     /* New message dialogue! */
  385.     LoaderDialogueBlk *newdialogue = _ldr_new_thread();
  386.     if(newdialogue == NULL)
  387.       return 1; /* claim event anyway */
  388.  
  389.     newdialogue->parent_listener = listener;
  390.     
  391.     /* Copy original DataSave message */
  392.     newdialogue->orig_datasave = malloc(message->hdr.size);
  393.     if(newdialogue->orig_datasave == NULL) {
  394.       RG("NoMem"); /* Oh dear, no memory could claimed! */
  395.       goto dialogue_abort;
  396.     }
  397.     memcpy(newdialogue->orig_datasave, message, message->hdr.size);
  398.  
  399.     {
  400.       int initial_bufferlen;
  401.       if(message->data.data_save.estimated_size <= 0) {
  402.         /* Bullshit, so decide buffer size ourself: */
  403.         initial_bufferlen = RAM_BUFFER_SIZE;
  404.       }
  405.       else {
  406.         /* Use estimated file size as RAM buffer size: */
  407.         initial_bufferlen = message->data.data_save.estimated_size;
  408.       
  409.         if(FLAG_SET(newdialogue->parent_listener->flags, LISTENER_SPRITEAREAS)
  410.         && message->data.data_save.file_type == FILETYPE_SPRITE)
  411.           /* (one extra word for sprite area length, written later) */
  412.           initial_bufferlen += sizeof(int);
  413.       }
  414.       
  415.       /* attempt to allocate desired amount of memory */
  416.       if(!flex_alloc((flex_ptr)&newdialogue->RAM_buffer, initial_bufferlen)) {
  417.         RG("NoMem"); /* Oh dear, no memory could claimed! */
  418.         goto dialogue_abort;
  419.       }
  420.       
  421.       /* send RAMFetch message (recorded, in case it bounces) */
  422.       message->hdr.size = 20 + sizeof(WimpRAMFetchMessage);
  423.       message->hdr.your_ref = message->hdr.my_ref;
  424.       message->hdr.action_code = Wimp_MRAMFetch;
  425.       nobudge_register(1024); /* disable flex budging (about to make static copy of pointer) */
  426.       message->data.ram_fetch.buffer = newdialogue->RAM_buffer;
  427.       message->data.ram_fetch.buffer_size = initial_bufferlen;
  428.     }
  429.  
  430.     if(FLAG_SET(newdialogue->parent_listener->flags, LISTENER_SPRITEAREAS)
  431.     && message->data.data_save.file_type == FILETYPE_SPRITE) {
  432.       /* prevent them filling the first word (reserved for sprite area length) */
  433.       message->data.ram_fetch.buffer = (void *)((int)message->data.ram_fetch.buffer + sizeof(int));
  434.       message->data.ram_fetch.buffer_size -= sizeof(int);
  435.     }
  436.  
  437.     if(E(wimp_send_message(Wimp_EUserMessageRecorded, message, message->hdr.sender, NULL, NULL)))
  438.       goto dialogue_abort;
  439.  
  440.     /* We don't actually know that they are RAM transfer capable yet */
  441.     newdialogue->RAM_capable = false;
  442.     newdialogue->last_message_ref = message->hdr.my_ref;
  443.     newdialogue->last_chunk_len = message->data.ram_fetch.buffer_size;
  444.     return 1; /* claim event */
  445.     
  446.     dialogue_abort:
  447.       RE(_ldr_kill_thread(newdialogue));
  448.       return 1; /* claim event */ 
  449.   }
  450.   else
  451.     /* Must use Scrap transfer protocol */
  452.     _ldr_replyto_datasave(message, listener);
  453.     
  454.   return 1; /* claim event */
  455. }
  456.  
  457. /* ----------------------------------------------------------------------- */
  458.  
  459. static void _ldr_replyto_datasave(WimpMessage *reply_to, LoaderListenerBlk *record_listener)
  460. {
  461.   /* Remember the leafname, to use when we receive a DataLoad - we DON'T overwrite datasave_leafname yet, as the message may not be sent successfully */
  462.   char *orig_leafname = malloc(strlen(reply_to->data.data_save.leaf_name)+1);
  463.   if(orig_leafname == NULL)
  464.     RG_RET("NoMem");
  465.   strcpy(orig_leafname, reply_to->data.data_save.leaf_name);
  466.  
  467.   /* send DataSaveAck message */
  468.   reply_to->hdr.your_ref = reply_to->hdr.my_ref;
  469.   reply_to->hdr.action_code = Wimp_MDataSaveAck;
  470.   reply_to->hdr.size = (sizeof(WimpMessage) - sizeof(reply_to->data.data_save_ack.leaf_name) + strlen("<Wimp$Scrap>")+1 + 3) & ~3; /* word-align message length */
  471.   /* We are not the Filer, so tell the file's saver that its data is not 'secure', i.e. is not going to end up in a permanent file: */
  472.   strncpy(reply_to->data.data_save_ack.leaf_name, "<Wimp$Scrap>", sizeof(reply_to->data.data_save_ack.leaf_name)-1);
  473.   reply_to->data.data_save_ack.estimated_size = -1;
  474.   if(!E(wimp_send_message(Wimp_EUserMessage, reply_to, reply_to->hdr.sender, NULL, NULL))) {
  475.  
  476.     /* record claiming listener & leafname with my_ref to match */
  477.     datasaveack_myref = reply_to->hdr.my_ref;
  478.     datasave_listener = record_listener;
  479.     free(datasave_leafname);datasave_leafname = orig_leafname;
  480.   }
  481. }
  482.  
  483. /* ----------------------------------------------------------------------- */
  484.  
  485. static int _ldr_dataloadopen_listener(WimpMessage *message, void *handle)
  486. {
  487.   /* We are a permanent DataLoad/Open listener (deals with Filer) */
  488.   bool delete_file = false, ack_immediately = false;
  489.   char *title;
  490.   LoaderListenerBlk *found_listener;
  491.   NOT_USED(handle)
  492.  
  493.   /* Is this a general broadcast? */
  494.   if(message->hdr.action_code == Wimp_MDataOpen) {
  495.   
  496.     /* This message should come out of the blue */
  497.     if(message->hdr.your_ref != 0)
  498.       M_RETV("TransUXreq", 0)
  499.  
  500.     found_listener = _ldr_find_broadcast_listener(message->data.data_open.file_type);
  501.     ack_immediately = true;
  502.     title = message->data.data_load.leaf_name;
  503.   }
  504.   else {
  505.  
  506.     /* do we already know the correct listener from a DataSave message? */
  507.     if(message->hdr.your_ref == datasaveack_myref) {
  508.       /* we have a leafname and listener for this message */
  509.       datasaveack_myref = -1; /* clear for next time */
  510.       title = datasave_leafname; 
  511.       found_listener = datasave_listener;
  512.     }
  513.     else {
  514.       /* We have no original leafname or listener for this message */
  515.       found_listener = _ldr_find_suitable_listener(message->data.data_load.destination_window, message->data.data_save.destination_icon, message->data.data_load.file_type, message->hdr.your_ref);
  516.       
  517.       /* If no proper leafname then substitute 'Untitled' for '<Wimp$Scrap>' */
  518.       if(message->hdr.your_ref != 0)
  519.         title = "<Untitled>";
  520.       else
  521.         title = message->data.data_load.leaf_name;
  522.     }
  523.     if(message->hdr.your_ref != 0)
  524.       delete_file = true;
  525.   }
  526.   if(found_listener == NULL) 
  527.     return 0; /* event not handled */
  528.  
  529.   if(ack_immediately) {
  530.     /* Acknowledge immediately */
  531.     message->hdr.your_ref = message->hdr.my_ref;
  532.     message->hdr.action_code = Wimp_MDataLoadAck;
  533.     if(E(wimp_send_message(Wimp_EUserMessage,message, message->hdr.sender, NULL,NULL)))
  534.       return 1; /* claim event */
  535.   }
  536.  
  537.   /* Call file loader */
  538.   void *input_buffer = NULL;
  539.   _kernel_oserror *err = NULL;
  540.   if(found_listener->loader_method == NULL) {
  541.     if(found_listener->file_type != FILETYPE_APP && found_listener->file_type != FILETYPE_DIR) {
  542.       /* Use standard file loader */
  543.       err = loader_buffer_file(
  544.         message->data.data_load.leaf_name,
  545.         (flex_ptr)&input_buffer,
  546.         (FLAG_SET(found_listener->flags, LISTENER_SPRITEAREAS) && found_listener->file_type == FILETYPE_SPRITE)
  547.       );
  548.     }
  549.   }
  550.   else /* Use client's custom file loader */
  551.     err = found_listener->loader_method(message->data.data_load.leaf_name, (flex_ptr)&input_buffer);
  552.  
  553.   if(err != NULL) {
  554.     /* Loading error */
  555.     err_report(err->errnum, msgs_lookupsub("LoadFail", err->errmess, NULL, NULL, NULL));
  556.     return 1; /* claim event */
  557.   }
  558.  
  559.   /* If follow-on DataLoad message then delete input file (more general than checking for "<wimp$scrap>") */
  560.   if(delete_file) {
  561.     /* OS_FSControl 27 is wipe object(s), flags bit 0 means recurse */
  562.     if(E(_swix(OS_FSControl,_INR(0,1)|_IN(3), 27, message->data.data_load.leaf_name, 1u<<0)))
  563.       goto post_load_error;
  564.   }
  565.  
  566.   if(!ack_immediately) {
  567.     /* Acknowledge loaded successfully (just a courtesy message, so we don't expect a reply) */
  568.     message->hdr.your_ref = message->hdr.my_ref;
  569.     message->hdr.action_code = Wimp_MDataLoadAck;
  570.     if(E(wimp_send_message(Wimp_EUserMessage,message, message->hdr.sender, NULL,NULL)))
  571.       goto post_load_error;
  572.   }
  573.  
  574.   /* File loaded successfully - call finished handler */
  575.   {
  576.     char *full_path;
  577.     if(!delete_file) {
  578.       if(E(loader_canonicalise(&full_path, NULL, NULL, title)))
  579.         goto post_load_error;
  580.       title = full_path;
  581.     }
  582.     found_listener->finished_method(title, !delete_file, (flex_ptr)&input_buffer, message->data.data_load.file_type, found_listener->client_handle);
  583.     if(!delete_file)
  584.       free(full_path);
  585.   }
  586.  
  587.   return 1; /* claim event */
  588.  
  589. post_load_error:
  590.   if(input_buffer != NULL)
  591.     flex_free((flex_ptr)&input_buffer);
  592.  
  593.   return 1; /* claim event */
  594. }
  595.  
  596. /* -----------------------------------------------------------------------
  597.                     Transient Wimp message handlers (for RAM transfer threads)
  598.  
  599. [Message handlers MUST claim messages that are directed at them (return 1), even if an error occurs]
  600. */
  601.  
  602. static int _ldr_ramfetch_bounce_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  603. {
  604.   /* Loading protocol - No reply to a RAMFetch message of ours */
  605.   LoaderDialogueBlk *talk = (LoaderDialogueBlk *)handle;
  606.   NOT_USED(event_code)
  607.   NOT_USED(id_block)
  608.  
  609.   if(event->user_message_acknowledge.hdr.action_code != Wimp_MRAMFetch
  610.   || event->user_message_acknowledge.hdr.my_ref != talk->last_message_ref)
  611.     return 0; /* if not bounced message of ours, pass event on */
  612.  
  613.   nobudge_deregister(); /* no need to protect RAM buffer anymore */
  614.   
  615.   if (!talk->RAM_capable)
  616.     /* Use FTP instead - reply to original DataSave msg */
  617.     _ldr_replyto_datasave(talk->orig_datasave, talk->parent_listener);
  618.  
  619.   RE(_ldr_kill_thread(talk))
  620.   return 1; /* claim event */
  621. }
  622.  
  623. /* ----------------------------------------------------------------------- */
  624.  
  625. static int _ldr_ramtransmit_handler(WimpMessage *message, void *handle)
  626. {
  627.   /* Loading protocol - Data has been written to buffer */
  628.   LoaderDialogueBlk *talk = (LoaderDialogueBlk *)handle;
  629.  
  630.   if(message->hdr.your_ref != talk->last_message_ref)
  631.     return 0; /* if not reply to our last message, pass event on */
  632.  
  633.   nobudge_deregister(); /* no need to protect RAM buffer anymore */
  634.  
  635.   if(!talk->RAM_capable)
  636.     /* Right, we have a RAM transfer dialogue */
  637.     talk->RAM_capable=true;
  638.  
  639.   /* Was that the last bufferful? */
  640.   if(message->data.ram_transmit.nbytes < talk->last_chunk_len) {
  641.     /* That was the last RAMTransmit message! :-) */
  642.  
  643.     /* Trim off any excess buffer that wasn't written to */
  644.     if(!flex_extend((flex_ptr)&talk->RAM_buffer, flex_size((flex_ptr)&talk->RAM_buffer) - talk->last_chunk_len + message->data.ram_transmit.nbytes)) {
  645.       RG("NoMem");
  646.       goto cleanexit;
  647.     }
  648.     if(FLAG_SET(talk->parent_listener->flags, LISTENER_SPRITEAREAS))
  649.       /* Fill in first word (sprite area size) of sprite area */
  650.       *talk->RAM_buffer = flex_size((flex_ptr)&talk->RAM_buffer);
  651.  
  652.     /* File loaded successfully - call finished handler */
  653.     talk->parent_listener->finished_method(talk->orig_datasave->data.data_save.leaf_name, false, (flex_ptr)&talk->RAM_buffer, talk->orig_datasave->data.data_save.file_type, talk->parent_listener->client_handle);
  654.     
  655.     /* clean up after this message dialogue */
  656.     RE(_ldr_kill_thread(talk));
  657.   }
  658.   else {
  659.     /* There is more data to come, yet...
  660.     extend the buffer for incoming data - preferably we want 256 bytes extra */
  661.     if(!flex_extend((flex_ptr)&talk->RAM_buffer, flex_size((flex_ptr)&talk->RAM_buffer) + RAM_BUFFER_EXTEND)) {
  662.       RG("NoMem");
  663.       goto cleanexit;
  664.     }
  665.     talk->last_chunk_len = RAM_BUFFER_EXTEND;
  666.  
  667.     /* OK, we have extended the buffer, so now we can send another RAMFetch message (recorded, in case it bounces) */
  668.     message->hdr.size = 20 + sizeof(WimpRAMFetchMessage);
  669.     message->hdr.your_ref = message->hdr.my_ref;
  670.     message->hdr.action_code = Wimp_MRAMFetch;
  671.     /* We want them to write at the end of the current data: */
  672.     nobudge_register(1024); /* disable flex budging (about to make static copy of pointer) */
  673.     message->data.ram_fetch.buffer = (void *)((int)talk->RAM_buffer + flex_size((flex_ptr)&talk->RAM_buffer) - RAM_BUFFER_EXTEND);
  674.     /* They have an extension of 256 bytes: */
  675.     message->data.ram_fetch.buffer_size = RAM_BUFFER_EXTEND;
  676.     if(E(wimp_send_message(Wimp_EUserMessageRecorded, message, message->hdr.sender, NULL, NULL)))
  677.       goto cleanexit;
  678.     
  679.     /* update our thread info block... */
  680.     talk->last_message_ref = message->hdr.my_ref;
  681.  
  682.   } /* was not last bufferful */
  683.   return 1; /* claim event */
  684.  
  685.   cleanexit:
  686.     RE(_ldr_kill_thread(talk)); /* clean up after this message dialogue */
  687.     return 1; /* claim event */
  688. }
  689.  
  690. /* -----------------------------------------------------------------------
  691.                          Miscellaneous internal functions
  692. */
  693.  
  694. static _kernel_oserror *_ldr_kill_listener(LoaderListenerBlk *kill_listener)
  695. {
  696.   LoaderDialogueBlk *scan_dialogues = dialogue_list;
  697.  
  698.   /* De-link Listener from list */
  699.   if(kill_listener == listener_list)
  700.     listener_list = kill_listener->next_listener;
  701.  
  702.   if(kill_listener->prev_listener != NULL)
  703.     kill_listener->prev_listener->next_listener = kill_listener->next_listener;
  704.  
  705.   if(kill_listener->next_listener != NULL)
  706.     kill_listener->next_listener->prev_listener = kill_listener->prev_listener;
  707.  
  708. #ifdef NO_STATIC_GADGETS_LIST
  709.   /* Remove array of gadget ids */
  710.   free(kill_listener->gadgets);
  711. #endif
  712.  
  713.   /* Remove any outstanding message handlers */
  714.   while(scan_dialogues != NULL) {
  715.     if(scan_dialogues->parent_listener == kill_listener) {
  716.       scan_dialogues = scan_dialogues->next_dialogue;
  717.       THROW(_ldr_kill_thread(scan_dialogues));
  718.     }
  719.     else
  720.       scan_dialogues = scan_dialogues->next_dialogue;
  721.   }
  722.  
  723.   /* Destroy it */
  724.   free(kill_listener);
  725.   return NULL; /* success */
  726. }
  727.  
  728. /* ----------------------------------------------------------------------- */
  729.  
  730. static LoaderListenerBlk *_ldr_find_broadcast_listener(int file_type)
  731. {
  732.   /* Search linked list to find if any Listeners are willing to start a thread to load this filetype */
  733.   LoaderListenerBlk *scan_list = listener_list;
  734.   while(scan_list != NULL) {
  735.     if(FLAG_SET(scan_list->flags, LISTENER_CLAIM) && (scan_list->file_type == file_type || scan_list->file_type == FILETYPE_ALL))
  736.       return scan_list; /* found a listener */
  737.     scan_list = scan_list->next_listener;
  738.   }
  739.   return NULL; /* failure */
  740. }
  741.  
  742. /* ----------------------------------------------------------------------- */
  743.  
  744. LoaderListenerBlk *_ldr_find_listener(int file_type, ObjectId window, ComponentId *gadgets)
  745. {
  746.   LoaderListenerBlk *scan_list = listener_list;
  747.   while(scan_list != NULL) {
  748. #ifdef NO_STATIC_GADGETS_LIST
  749.     if(scan_list->object == window
  750.     && scan_list->file_type == file_type) {
  751.       if(gadgets == NULL && scan_list->gadgets == NULL)
  752.         return scan_list; /* got a match */
  753.       else {
  754.         if(gadgets != NULL && scan_list->gadgets != NULL) {
  755.           /* Scan arrays until reach end or no match */
  756.           int i = -1;
  757.           do {
  758.             i++;
  759.             if(gadgets[i] != scan_list->gadgets[i])
  760.               goto next_listener; /* no match */
  761.           } while(gadgets[i] != NULL_ComponentId && scan_list->gadgets[i] != NULL_ComponentId);
  762.           return scan_list; /* matched all the way to the end */
  763.         }
  764.       }
  765.     }
  766.     next_listener:
  767. #else
  768.     if(scan_list->object == window
  769.     && scan_list->file_type == file_type
  770.     && scan_list->gadgets == gadgets)
  771.       return scan_list; /* got a match */  
  772. #endif
  773.     scan_list = scan_list->next_listener;
  774.   }
  775.   return NULL; /* too bad, can't find it */
  776. }
  777.  
  778. /* ----------------------------------------------------------------------- */
  779.  
  780. static bool _ldr_check_dropzone(ObjectId object, ComponentId *gadgets,int window_handle, int icon_number)
  781. {
  782.   /* Check the dropzone for this listener */
  783.  
  784.   if(object == NULL_ObjectId)
  785.     return true; /* they don't care which object */
  786.  
  787.   /* Is it a Window object or an Iconbar object? */
  788.   {
  789.     ObjectClass objclass;
  790.     {
  791.       _kernel_oserror *errptr;
  792.       errptr=toolbox_get_object_class(0, object, &objclass);
  793.       if(errptr != NULL) {
  794.         if(errptr->errnum != ERR_BAD_OBJECT_ID)
  795.           err_complain(errptr->errnum, errptr->errmess);
  796.         return false; /* ignore listener if bad object ID */
  797.       }
  798.     }
  799.       
  800.     if(objclass == Iconbar_ObjectClass) {
  801.       /* It is an Iconbar object */
  802.       if(window_handle != -2)
  803.         return false; /* not iconbar drop */
  804.  
  805.       {
  806.         int ls_iconh;
  807.         E_RETV(iconbar_get_icon_handle(0, object, &ls_iconh), false);
  808.         if(icon_number != ls_iconh)
  809.           return false; /* wrong icon */
  810.       }
  811.       return true; /* drop on right iconbar icon */
  812.     }
  813.   }
  814.  
  815.   /* It is a Window object - get Wimp window handle */
  816.   {
  817.     int ls_windowh;
  818.     E_RETV(window_get_wimp_handle(0, object, &ls_windowh), false);
  819.     if(window_handle != ls_windowh)
  820.       return false; /* wrong window handle */
  821.   }
  822.  
  823.   if(gadgets == NULL)
  824.     return true; /* they don't care what gadget */
  825.  
  826.   for(int j = 0;gadgets[j] != NULL_ComponentId;j++) {
  827.     /* get list size */
  828.     int nbytes;
  829.     _kernel_oserror *errptr = gadget_get_icon_list(0, object, gadgets[j], NULL, 0, &nbytes);
  830.     if(errptr != NULL) {
  831.       /* ignore gadget if bad gadget ID */ 
  832.       if(errptr->errnum != ERR_BAD_COMPONENT_ID)
  833.         err_complain(errptr->errnum, errptr->errmess);
  834.     }
  835.     else {
  836.       /* allocate buffer for list */
  837.       int *icons_list = malloc(nbytes);
  838.       if(icons_list == NULL)
  839.         RG_RETV("NoMem", false);
  840.         
  841.       /* read icon numbers into buffer */
  842.       if(E(gadget_get_icon_list(0, object, gadgets[j], icons_list, nbytes, &nbytes))) {
  843.         free(icons_list);
  844.         return false;
  845.       }
  846.       for (int i = 0;i < (nbytes>>2);i++) {
  847.         if(icon_number == icons_list[i]) {
  848.           free(icons_list);
  849.           return true; /* dropped in right place */
  850.         }
  851.       }
  852.       free(icons_list);
  853.     }
  854.   }
  855.   return false; /* none of those icons */
  856. }
  857.  
  858. /* ----------------------------------------------------------------------- */
  859.  
  860. static LoaderListenerBlk *_ldr_find_suitable_listener(int window_handle, int icon_number, int file_type, int tempfile)
  861. {
  862.   /* Search linked list to find if any Listeners are willing to start a thread to load this dropzone/filetype/source */
  863.   
  864.   bool filetype_handled = false, dropzone_handled = false;
  865.   {
  866.     LoaderListenerBlk *scan_list = listener_list;   
  867.     while(scan_list != NULL) {
  868.       bool good_dropzone = _ldr_check_dropzone(scan_list->object, scan_list->gadgets, window_handle, icon_number);
  869.       
  870.       /* Check the filetype accepted by this listener */
  871.       if(file_type == scan_list->file_type || scan_list->file_type == FILETYPE_ALL) {
  872.         filetype_handled = true;
  873.         
  874.         if(good_dropzone) {
  875.           /* We have found a listener for the dropzone */
  876.        
  877.           if(!FLAG_SET(scan_list->flags, LISTENER_FILEONLY) || !tempfile)
  878.             /* either willing to do !Scrap transfer, or this isn't !Scrap transfer */
  879.             return scan_list; /* this is an entirely suitable listener */
  880.           else {
  881.             /* wot a bummer */
  882.             if(!FLAG_SET(_ldr_flags, LOADER_QUIET_NOTPERM))
  883.               M("FileNotPerm");
  884.             return NULL; /* suitable listener not found */
  885.           }
  886.         }
  887.       }
  888.       if(good_dropzone)
  889.         dropzone_handled = true; /* At least one listener likes it */
  890.           
  891.       scan_list = scan_list->next_listener;
  892.     }
  893.   }
  894.   /* Oh dear, what went wrong?... */
  895.   if(!filetype_handled) {
  896.     if(!FLAG_SET(_ldr_flags,LOADER_QUIET_BADTYPE))
  897.       M("NoSuchFileType");
  898.     return NULL; /* suitable listener not found */
  899.   }
  900.   /* -> There exist dropzones that handle that filetype */
  901.   if(!dropzone_handled) {
  902.     if(!FLAG_SET(_ldr_flags, LOADER_QUIET_BADDROP))
  903.       M("NoSuchDropZone");
  904.     return NULL; /* suitable listener not found */
  905.   }
  906.   /* -> File dropped on valid dropzone, though not necessarily for that filetype */
  907.   if(!FLAG_SET(_ldr_flags, LOADER_QUIET_BADDROP))
  908.     M("WrongZone");
  909.   return NULL; /* suitable listener not found */
  910. }
  911.  
  912. /* ----------------------------------------------------------------------- */
  913.  
  914. static LoaderDialogueBlk *_ldr_new_thread(void)
  915. {
  916.   /* Allocate data block for new RAM transfer thread and link into list
  917.      Do NOT call _ldr_kill_thread() if this function fails! */
  918.   LoaderDialogueBlk *new_dialogue = malloc(sizeof(LoaderDialogueBlk));
  919.   if(new_dialogue == NULL)
  920.     RG_RETV("NoMem", NULL);
  921.   new_dialogue->RAM_buffer = NULL; /* no flex block here */
  922.   new_dialogue->orig_datasave = NULL; /* no malloc block here */
  923.   new_dialogue->next_dialogue = dialogue_list; /* add to front of list */
  924.  
  925.   dialogue_list = new_dialogue;
  926.   new_dialogue->prev_dialogue = NULL; /* no previous record */
  927.  
  928.   /* Register handlers */
  929.   if(E(event_register_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)new_dialogue)))
  930.     goto cockup;
  931.  
  932.   if(!E(event_register_wimp_handler(-1, Wimp_EUserMessageAcknowledge, _ldr_ramfetch_bounce_handler, (void *)new_dialogue)))
  933.     return new_dialogue; /* success */
  934.  
  935.   /* We virtuously tidy up our own mess */
  936.   event_deregister_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)new_dialogue);
  937.   cockup:
  938.     free(new_dialogue);
  939.     return NULL; /* failure */
  940. }
  941.  
  942. /* ----------------------------------------------------------------------- */
  943.  
  944. static _kernel_oserror *_ldr_kill_thread(LoaderDialogueBlk *kill_me)
  945. {
  946.   /* Free data block for RAM transfer thread and delink */
  947.   if(kill_me == dialogue_list)
  948.     dialogue_list = kill_me->next_dialogue;
  949.   
  950.   if(kill_me->prev_dialogue != NULL)
  951.     kill_me->prev_dialogue->next_dialogue = kill_me->next_dialogue;
  952.     
  953.   if(kill_me->next_dialogue != NULL)
  954.     kill_me->next_dialogue->prev_dialogue = kill_me->prev_dialogue;
  955.     
  956.   /* Free any associated data blocks */
  957.   free(kill_me->orig_datasave); /* does nothing if NULL pointer */
  958.   if(kill_me->RAM_buffer != NULL)
  959.     flex_free((flex_ptr)&kill_me->RAM_buffer);
  960.   free(kill_me);
  961.  
  962.   /* Deregister handlers */
  963.   THROW(event_deregister_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)kill_me));
  964.   THROW(event_deregister_wimp_handler(-1, Wimp_EUserMessageAcknowledge, _ldr_ramfetch_bounce_handler, (void *)kill_me));
  965.  
  966.   return NULL; /* success */
  967. }
  968.